function EditPassword(param) {
try{
	var that = this;

	var CUR_PASSWORD_CHECK_URL = "/zzzzzz/user/is_password_correct";

	this.j_password_edit_form = null;
	this.j_password_edit_cur = null;
	this.j_password_edit_new = null;
	this.j_password_edit_re_new = null;
	this.j_password_edit_cur_error_msg = null;
	this.j_password_edit_new_error_msg = null;
	this.j_password_edit_re_new_error_msg = null;
	this.j_password_edit_submit = null;
	this.j_password_edit_submit_status = null;
	
	var cur_password_correct = false;
	var new_password_correct = false;
	var re_new_password_correct = false;
	
	var cur_password_validation_locked = true;
	var new_password_validation_locked = true;
	var re_new_password_validation_locked = true;

	var cur_password_validation_ever_opened = false;
	var new_password_validation_ever_opened = false;
	var re_new_password_validation_ever_opened = false;
	
	var cur_password_correct_validating = false;
	var cur_password_correct_validated = false;
	
	var form_submiting = false;
	
	var jqxhr_cur_password = null;
	
	function __construct(p) {
	try{
		for(pkey in p) {
			if(pkey[0] != "_") {
				that[pkey] = p[pkey];
			}
		}
		
		that.j_password_edit_form.submit(editFormSubmit);
		that.j_password_edit_submit.mousedown(editSumbitMousedown);
		
		that.j_password_edit_new.blur(newPasswordInputBlur);
		that.j_password_edit_re_new.blur(reNewPasswordInputBlur);

		that.j_password_edit_cur.change(curPasswordInputChange);
		that.j_password_edit_new.change(newPasswordInputChange);
		that.j_password_edit_re_new.change(reNewPasswordInputChange);
		
	}catch(err){
		alert(err);
	}
	}
	
	function reset() {
		cur_password_correct = false;
		new_password_correct = false;
		re_new_password_correct = false;
		
		cur_password_validation_locked = true;
		new_password_validation_locked = true;
		re_new_password_validation_locked = true;

		cur_password_validation_ever_opened = false;
		new_password_validation_ever_opened = false;
		re_new_password_validation_ever_opened = false;
		
		cur_password_correct_validating = false;
		cur_password_correct_validated = false;
		
		form_submiting = false;
		
		jqxhr_cur_password = null;
		
		that.j_password_edit_cur.val("");
		that.j_password_edit_new.val("");
		that.j_password_edit_re_new.val("");
	}
	
	function curPasswordInputChange() {
		try{
			if(cur_password_validation_locked) {
				cur_password_validation_locked = false;
			}
		}catch(err){
			alert(err);
		}
	}
	
	function newPasswordInputChange() {
	try{
		if(new_password_validation_locked) {
			new_password_validation_locked = false;
		}
	}catch(err){
		alert(err);
	}
	}
	
	function newPasswordInputBlur() {
	try{
		if(!form_submiting) {
			validateNewPassword();
			validateReNewPassword();
		}
	}catch(err){
		alert(err);
	}
	}
	
	function reNewPasswordInputChange() {
		try{
			if(re_new_password_validation_locked) {
				re_new_password_validation_locked = false;
			}
		}catch(err){
			alert(err);
		}
	}
	
	function reNewPasswordInputBlur() {
		try{
			if(!form_submiting) {
				validateReNewPassword();
			}
		}catch(err){
			alert(err);
		}
	}
	
	function validatePasswordDiff() {
	try{	
		var cur_password = that.j_password_edit_cur.val();
		var newPassword = that.j_password_edit_new.val();
		
		if(cur_password === newPassword) {
			that.j_password_edit_new.data({ 'errorMsg' : "Password must differ from old password" });
			showWarningMsg(that.j_password_edit_new, that.j_password_edit_new_error_msg);
			return false;
		}else {
			removeWarningMsg(that.j_password_edit_new, that.j_password_edit_new_error_msg);
			return true;
		}
	}catch(err){
		alert(err);
	}
	}
	
	function validateNewPassword() {
	try{
		if(!new_password_validation_locked) {
			new_password_validation_locked = true;
	    	if(!new_password_validation_ever_opened) {
	    		new_password_validation_ever_opened = true;
	    	}
			
			var newPassword = that.j_password_edit_new.val();
			var newPassLength = jq.trim(newPassword).length;
			
			// check required
			if(!newPassLength) {
				new_password_correct = false;
				that.j_password_edit_new.data({ 'errorMsg' : "Value required" });
				showWarningMsg(that.j_password_edit_new, that.j_password_edit_new_error_msg);
				return false;
			}
			// check minimum length
			if(newPassLength < 8) {
				new_password_correct = false;
				that.j_password_edit_new.data({ 'errorMsg' : "At least 8 characters" });
				showWarningMsg(that.j_password_edit_new, that.j_password_edit_new_error_msg);
				return false;
			}
			// check maximum length
			if(newPassLength > 20) {
				new_password_correct = false;
				that.j_password_edit_new.data({ 'errorMsg' : "Less than 20 characters" });
				showWarningMsg(that.j_password_edit_new, that.j_password_edit_new_error_msg);
				return false;
			}
			// check regex
			if(newPassword.search(/^[a-zA-Z0-9_]{6,20}$/) == -1) {
				new_password_correct = false;
				that.j_password_edit_new.data({ 'errorMsg' : "Not a valid password" });
				showWarningMsg(that.j_password_edit_new, that.j_password_edit_new_error_msg);
				return false;
			}
			
			new_password_correct = true;
			removeWarningMsg(that.j_password_edit_new, that.j_password_edit_new_error_msg);
			
		}else {
			// do nothing
		}
	}catch(err){
		alert(err);
	}
	}
	
	function validateReNewPassword() {
	try{
		if(!re_new_password_validation_locked) {
			// once open, never lock
	    	if(!re_new_password_validation_ever_opened) {
	    		re_new_password_validation_ever_opened = true;
	    	}
	    	
			var reNewPassword = that.j_password_edit_re_new.val();
			var reNewPassLength = jq.trim(reNewPassword).length;

			// check required
			if(!reNewPassLength) {
				re_new_password_correct = false;
				that.j_password_edit_re_new.data({ 'errorMsg' : "Value required" });
				showWarningMsg(that.j_password_edit_re_new, that.j_password_edit_re_new_error_msg);
				return false;
			}
			
			var newPassword = that.j_password_edit_new.val();
			
			if(newPassword != reNewPassword) {
				re_new_password_correct = false;
				that.j_password_edit_re_new.data({ 'errorMsg' : "Passwords do not match" });
				showWarningMsg(that.j_password_edit_re_new, that.j_password_edit_re_new_error_msg);
				return false;
			}
			re_new_password_correct = true;
			removeWarningMsg(that.j_password_edit_re_new, that.j_password_edit_re_new_error_msg);
		}
	}catch(err){
		alert(err);
	}
	}
	
	function validateCurPassword() {
	try{
		if(!cur_password_validation_locked) {
			cur_password_validation_locked = true;
	    	if(!cur_password_validation_ever_opened) {
	    		cur_password_validation_ever_opened = true;
	    	}
			var cur_password = that.j_password_edit_cur.val();
			var curPassLength = jq.trim(cur_password).length;
			// check required
			if(!curPassLength) {
				cur_password_correct = false;
				that.j_password_edit_cur.data({ 'errorMsg' : "Password was incorrect" });
				showWarningMsg(that.j_password_edit_cur, that.j_password_edit_cur_error_msg);
				cur_password_correct_validated = true;
		    	cur_password_correct_validating = false;
				return false;
			}
			// server side check...
			if(cur_password_correct_validating) {
				jqxhr_cur_password.abort();
			}
			cur_password_correct_validating = true;
			jqxhr_cur_password = jq.post(CUR_PASSWORD_CHECK_URL, { 'cur_password' : cur_password }, validateCurPasswordSuccess)
			.error(function() {
		    	alert("Oops! An error occurred while processing your request.");
				cur_password_correct_validated = true;
				cur_password_correct_validating = false;
		    })
		    .complete(function() {
		   		// complete
				cur_password_correct_validated = true;
		    	cur_password_correct_validating = false;
		    });

		}else {
			cur_password_correct_validated = true;
			cur_password_correct_validating = false;
		}
	}catch(err){
		alert(err);
	}
	}
	
	function validateCurPasswordSuccess(data) {
	try{
		if(data === "OK") {
			cur_password_correct = true;
			removeWarningMsg(that.j_password_edit_cur, that.j_password_edit_cur_error_msg);
			cur_password_correct_validated = true;
			cur_password_correct_validating = false;
		}else if(data === "INCORRECT") {
			cur_password_correct = false;
			that.j_password_edit_cur.data({ 'errorMsg' : "Password was incorrect" });
			showWarningMsg(that.j_password_edit_cur, that.j_password_edit_cur_error_msg);
			cur_password_correct_validated = true;
			cur_password_correct_validating = false;
		}else {
			cur_password_correct = false;
			that.j_password_edit_cur.data({ 'errorMsg' : "Error" });
			showWarningMsg(that.j_password_edit_cur, that.j_password_edit_cur_error_msg);
			cur_password_correct_validated = true;
			cur_password_correct_validating = false;
		}
	}catch(err){
		alert(err);
	}
	}
	
	function editSumbitMousedown(e) {
	try{
		form_submiting = true;

		// reset
		cur_password_correct_validated = false;
		
	}catch(err){
		alert(err);
	}
	}
	
	function editFormSubmit(e) {
	try{
    	e.preventDefault();
    	
    	if(!new_password_validation_ever_opened) {
    		new_password_validation_ever_opened = true;
			new_password_validation_locked = false;
    	}
    	if(!re_new_password_validation_ever_opened) {
        	re_new_password_validation_ever_opened = true;
    		re_new_password_validation_locked = false;
    	}
    	
    	validateNewPassword();
    	validateReNewPassword();
    	
    	if(new_password_correct && re_new_password_correct) {
    		if(validatePasswordDiff()) {
        		// start checking server side
    			showLoadingImage(that.j_password_edit_submit_status);

    	    	if(!cur_password_validation_ever_opened) {
    	    		cur_password_validation_ever_opened = true;
    				cur_password_validation_locked = false;
    	    	}
    	    	
        		validateCurPassword();
    			isCurPasswordValidated();
    		}else {
    			form_submiting = false;
    		}
    	}else {
			form_submiting = false;
		}
    	
	}catch(err){
		alert(err);
	}
	}
	
	function isCurPasswordValidated() {
	try{
		if(cur_password_correct_validated) {
			form_submiting = false;
			hideLoadingImage(that.j_password_edit_submit_status);
			if(cur_password_correct) {
				completeFormSubmit();
				return true;
			}
			return false;
		}else {
			setTimeout( function() {
				isCurPasswordValidated();
			}, 300 );
		}
	}catch(err){
		alert(err);
	}
	}
	
	function completeFormSubmit() {
	try{
		var url = that.j_password_edit_form.attr("action");
		var newPassword = that.j_password_edit_new.val();
		jq.post(url, { "new_password": newPassword },
			function(data) {
				if(data === "SUCCESS") {
					alert("Your request has been processed successfully!");
				}else if(data === "AUTHENTICATE") {
					alert("Unauthenticated!");
				}
				else {
			    	alert("Oops! An error occurred while processing your request.");
				}
			}
		)
		.error(function() {
	    	alert("Oops! An error occurred while processing your request.");
	    })
    	.complete(function() {
   			// complete
    		reset();
    	});
		
	}catch(err){
		alert(err);
	}
	}
	
	function showLoadingImage(el) {
		if(!el.is(":visible")) {
			el.show();
		}
	}
		
	function hideLoadingImage(el) {
		if(el.is(":visible")) {
			el.hide();
		}
	}
	
	function showWarningMsg(inputEl, errEl) {
	try{
		if (!inputEl.hasClass("error")){
			inputEl.addClass("error");
		}
		errEl.text(inputEl.data("errorMsg"));
		if(!errEl.is(":visible")) {
			errEl.show();
		}
	}catch(err){
		alert(err);
	}
	}
	
	function removeWarningMsg(inputEl, errEl) {
	try{
		if(inputEl.hasClass("error")) {
			inputEl.removeClass("error");
		}
		if(errEl.is(":visible")) {
			errEl.hide();
		}
	}catch(err){
		alert(err);
	}
	}
	
	__construct(param);
	
	
}catch(err){
	alert(err);
}
}
